package com.hmdp.service.impl;

import java.time.LocalDateTime;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.common.HostHolder;
import com.hmdp.common.R;
import com.hmdp.common.RedisIdWorker;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.SeckillVoucherMapper;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.IVoucherOrderService;

/**
 * @author: YeRenping
 * @Date: 2023/2/19
 * @Description:
 */
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    private final VoucherOrderMapper voucherOrderMapper;
    private final SeckillVoucherMapper seckillVoucherMapper;
    private final RedisIdWorker redisIdWorker;


    @Autowired
    public VoucherOrderServiceImpl(VoucherOrderMapper voucherOrderMapper, SeckillVoucherMapper seckillVoucherMapper, RedisIdWorker redisIdWorker) {
        this.voucherOrderMapper = voucherOrderMapper;
        this.seckillVoucherMapper = seckillVoucherMapper;
        this.redisIdWorker = redisIdWorker;
    }


    @Override
    @Transactional
    public R seckillVoucher(Long voucherId) {
        // 判断活动是否开始
        SeckillVoucher seckillVoucher = seckillVoucherMapper.selectById(voucherId);
        if (LocalDateTime.now().isBefore(seckillVoucher.getBeginTime())) {
            return R.fail("活动未开始");
        }
        if (LocalDateTime.now().isAfter(seckillVoucher.getEndTime())) {
            return R.fail("活动已结束");
        }
        // 判断库存是否充足
        Integer stock = seckillVoucher.getStock();
        if (stock<=0){
            return R.fail("库存不足");
        }
        // 扣减库存
        int i =  seckillVoucherMapper.updateStock(new SeckillVoucher().setVoucherId(voucherId).setStock(stock));
        if (i <= 0){
            return R.fail("操作繁忙");
        }
        // 生成订单
        Long userId = HostHolder.getUser().getId();
        long orderId = redisIdWorker.nextId("order");
        VoucherOrder voucherOrder = new VoucherOrder()
                .setId(orderId)
                .setVoucherId(voucherId)
                .setUserId(userId);
        int i2 = voucherOrderMapper.addOrder(voucherOrder);
        if (i2<=0){
            return R.fail("操作繁忙");
        }
        return R.ok(orderId);
    }

    /**
     * 一人一单业务处理
     * 在高并发下依旧存在“一人下多单的问题”
     * @param voucherId
     * @return
     */
    @Override
    @Transactional
    public R seckillVoucherForOneBuyOne(Long voucherId) {
        // 判断活动是否开始
        SeckillVoucher seckillVoucher = seckillVoucherMapper.selectById(voucherId);
        if (LocalDateTime.now().isBefore(seckillVoucher.getBeginTime())) {
            return R.fail("活动未开始");
        }
        if (LocalDateTime.now().isAfter(seckillVoucher.getEndTime())) {
            return R.fail("活动已结束");
        }
        // 判断库存是否充足
        Integer stock = seckillVoucher.getStock();
        if (stock<=0){
            return R.fail("库存不足");
        }
        Long userId = HostHolder.getUser().getId();
        // 一人一单
        // 一人一单
        Long i = voucherOrderMapper.selectByUserId(new VoucherOrder().setUserId(userId));
        if (i > 0) {
            return R.fail("您已经抢购了一次，请不要重复下单");
        }
        // 扣减库存
        int i2 = seckillVoucherMapper.updateStock(new SeckillVoucher().setVoucherId(voucherId).setStock(stock));
        if (i2 <= 0) {
            return R.fail("操作繁忙");
        }
        // 生成订单
        long orderId = redisIdWorker.nextId("order");
        VoucherOrder voucherOrder = new VoucherOrder()
                .setId(orderId)
                .setVoucherId(voucherId)
                .setUserId(userId);
        int i3 = voucherOrderMapper.addOrder(voucherOrder);
        if (i3 <= 0) {
            return R.fail("操作繁忙");
        }
        return R.ok(orderId);
    }

    /**
     * 一人一单业务处理
     * 彻底解决一人一单问题
     * （悲观锁：1、查订单->判断订单、2、扣减库存 3、生成订单）
     * @param voucherId
     * @return
     */
    @Override
    public R seckillVoucherForOneBuyOnePessimisticLock(Long voucherId) {
        // 判断活动是否开始
        SeckillVoucher seckillVoucher = seckillVoucherMapper.selectById(voucherId);
        if (LocalDateTime.now().isBefore(seckillVoucher.getBeginTime())) {
            return R.fail("活动未开始");
        }
        if (LocalDateTime.now().isAfter(seckillVoucher.getEndTime())) {
            return R.fail("活动已结束");
        }
        // 判断库存是否充足
        Integer stock = seckillVoucher.getStock();
        if (stock<=0){
            return R.fail("库存不足");
        }
        Long userId = HostHolder.getUser().getId();
        synchronized (userId.toString().intern()) {
            // 当道当前对象的代理对象(防止spring事务失效)
            IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy();
            return proxy.createOrder(voucherId, stock, userId);
        }
    }

    /**
     * 创建一比订单
     * @param voucherId
     * @param stock
     * @param userId
     * @return
     */
    @Override
    @Transactional
    public R createOrder(Long voucherId, Integer stock, Long userId) {
        /**
         * 以用户id 进行加锁，而不是加载整个方法上，锁定的范围减少了
         * 同一个用户加一把锁
         * 不同的用户加不同的锁
         */
            // 一人一单
            Long i = voucherOrderMapper.selectByUserId(new VoucherOrder().setUserId(userId));
            if (i > 0) {
                return R.fail("您已经抢购了一次，请不要重复下单");
            }
            // 扣减库存
            int i2 = seckillVoucherMapper.updateStock(new SeckillVoucher().setVoucherId(voucherId).setStock(stock));
            if (i2 <= 0) {
                return R.fail("操作繁忙");
            }
            // 生成订单
            long orderId = redisIdWorker.nextId("order");
            VoucherOrder voucherOrder = new VoucherOrder()
                    .setId(orderId)
                    .setVoucherId(voucherId)
                    .setUserId(userId);
            int i3 = voucherOrderMapper.addOrder(voucherOrder);
            if (i3 <= 0) {
                return R.fail("操作繁忙");
            }
            return R.ok(orderId);
        }
    }


